package gis

import (
	"bytes"
	"dgo/goutils/utils"
	"fmt"
	"gitee.com/ymofen/gobase"
	"strings"
)

/*
使用格网点内插计算值
*/
type GisInterpose struct {
	invalidValue float64

	gridOpt *TGisGridV3
	// 加载有效数据
	lst map[string]*interposBaseItemVal
}

func NewGisInterpos() *GisInterpose {
	rval := &GisInterpose{}
	return rval
}

type interposBaseItemVal struct {
	Id   string
	B, L float64
	V    float64
}

func (this *GisInterpose) Status() string {
	opt := this.gridOpt
	lst := this.lst
	if lst == nil || opt == nil {
		return "NULL"
	}
	return fmt.Sprintf("cnt=%d;startlng=%.4f;startlat:%.4f;gridlng=%.4f;gridlat=%.4f", len(lst), opt.StartLng, opt.StartLat, opt.GridLng, opt.GridLat)
}

func (this *GisInterpose) SetInvalidValue(v float64) bool {
	if this.invalidValue != v {
		this.invalidValue = v
		return true
	}
	return false
}

func (this *GisInterpose) findBaseItemByBL(b, l float64) *interposBaseItemVal {
	keystr := fmt.Sprintf("%.4f-%.4f", l, b)
	return this.findBaseItem(keystr)
}

func (this *GisInterpose) findBaseItem(id string) *interposBaseItemVal {
	if this.lst == nil || len(this.lst) == 0 {
		return nil
	}
	return this.lst[id]
}

func (this *GisInterpose) GetValue(b, l float64, sb *utils.BytesBuilder) (ok bool, v float64) {
	lst := this.lst
	gridOpt := this.gridOpt
	if lst == nil {
		return
	}
	plst := gridOpt.FindNearest4Grid(b, l)
	return this.InnerGetValue(plst, l, b, sb)
}

func (this *GisInterpose) InnerGetValue(gridlst [4]BLPoint, l, b float64, sb *utils.BytesBuilder) (ok bool, v1 float64) {

	t0, t1, f0, f1 := gridlst[0], gridlst[1], gridlst[2], gridlst[3]
	lst1 := make([]*interposBaseItemVal, 0, 4)
	top0_rec := this.findBaseItemByBL(t0.B, t0.L)
	top1_rec := this.findBaseItemByBL(t1.B, t1.L)
	flr0_rec := this.findBaseItemByBL(f0.B, f0.L)
	flr1_rec := this.findBaseItemByBL(f1.B, f1.L)

	if top0_rec != nil {
		lst1 = append(lst1, top0_rec)
	}
	if top1_rec != nil {
		lst1 = append(lst1, top1_rec)
	}
	if flr0_rec != nil {
		lst1 = append(lst1, flr0_rec)
	}
	if flr1_rec != nil {
		lst1 = append(lst1, flr1_rec)
	}

	if len(lst1) > 0 {

		// 距离集合
		dlst := make([]float64, len(lst1))
		var sum_1_di float64
		for i := 0; i < len(lst1); i++ {
			itm := lst1[i]
			dlst[i] = CalcuDis(l, b, itm.L, itm.B) // 必须为正数
			if dlst[i] == 0 {
				dlst[i] = 0.00001 // 如果为0, 其实可以直接取偏差,
			}
			sum_1_di += 1 / dlst[i]

		}

		// 权重, 距离越近，权重越高, 如果不够平滑 可以加上平方
		wlst := make([]float64, len(lst1))
		for i := 0; i < len(dlst); i++ {
			wlst[i] = (1 / dlst[i]) / sum_1_di
		}

		var dv float64
		for i := 0; i < len(lst1); i++ {
			itm := lst1[i]
			v0 := itm.V
			dv += wlst[i] * v0
			if sb != nil {
				sb.Appendf("格网点:[%s],值:%.2f, 权重:%.2f,距离:%.2f, 计算值:%.4f\r\n", lst1[i].Id, lst1[i].V, wlst[i], dlst[i], dv)
			}
		}

		// 保留4位小数
		v1 = dv
		ok = true
	}
	return
}

func (this *GisInterpose) CheckReloadFromBuf(dataBuf []byte, datatype string) (valid bool, cnt int) {
	valid, startlng, startlat, gridlng, gridlat := this.CheckBufValid(dataBuf, datatype)
	if !valid || gridlng == 0 && gridlat == 0 {
		return false, 0
	}
	gridOpt := MakeGridV3(startlat, startlng, gridlat, gridlng)
	lst := make(map[string]*interposBaseItemVal)
	cnt = this.BaseValueFromBuf(lst, dataBuf)
	this.lst = lst
	this.gridOpt = &gridOpt
	return
}

/*
读前面的注释, 读取类型和格网点
也可以由外部直接设定
*/
func (this *GisInterpose) CheckBufValid(dataBuf []byte, datatype string) (valid bool, startlng, startlat, gridlng, gridlat float64) {
	reader := bytes.NewReader(dataBuf)
	var kvMap = make(gobase.StrMap)
	utils.ReadPerLine(reader, func(idx int64, line []byte) bool {
		str := utils.Trim(string(line))
		if len(str) == 0 {
			return true
		}
		if str[0] == '#' {
			str = utils.Trim(str[1:])
			kvMap.ParseKVPairs(str, "=", ";")
			if !valid {
				strtype := kvMap.StringByName("type", "")
				if strtype == datatype {
					valid = true
				}
			}
			return true
		}
		return false
	})
	gridlat = kvMap.Float64ByName("gridlat", 0)
	gridlng = kvMap.Float64ByName("gridlng", 0)
	startlat = kvMap.Float64ByName("startlat", 0)
	startlng = kvMap.Float64ByName("startlng", 0)
	return
}

func (this *GisInterpose) BaseValueFromString(lst map[string]*interposBaseItemVal, str string) *interposBaseItemVal {
	strs := strings.Split(str, ",")
	if len(strs) >= 2 {
		l, b, _ := utils.Str2Num3Ex(strs[0], "-")
		if b == 0 || l == 0 {
			return nil
		}

		v := utils.StrToFloat64Def(strs[1], this.invalidValue)
		if v == this.invalidValue {
			return nil
		}

		keystr := fmt.Sprintf("%.4f-%.4f", l, b)
		itm := &interposBaseItemVal{Id: keystr}
		itm.B = b
		itm.L = l
		itm.V = v
		lst[keystr] = itm
		return itm
	}
	return nil
}

func (this *GisInterpose) BaseValueFromBuf(lst map[string]*interposBaseItemVal, dataBuf []byte) (cnt int) {
	reader := bytes.NewReader(dataBuf)
	utils.ReadPerLine(reader, func(idx int64, line []byte) bool {
		str := utils.Trim(string(line))
		if len(str) == 0 {
			return true
		}
		if str[0] == '#' {
			return true
		}

		// 120.9733-54.5681,height
		if this.BaseValueFromString(lst, str) != nil {
			cnt++
		}
		return true
	})
	return
}

func (this *GisInterpose) BaseValueFromBufEx(lst map[int]int32, dataBuf []byte) (cnt int) {
	reader := bytes.NewReader(dataBuf)
	utils.ReadPerLine(reader, func(idx int64, line []byte) bool {
		str := utils.Trim(string(line))
		if len(str) == 0 {
			return true
		}
		if str[0] == '#' {
			return true
		}

		strs := strings.Split(str, ",")
		if len(strs) >= 2 {
			l, b, _ := utils.Str2Num3Ex(strs[0], "-")
			if b != 0 && l != 0 {
				key := int(b*1000)<<32 + int(l*1000)
				v := utils.StrToFloat64Def(strs[1], this.invalidValue)
				if v != this.invalidValue {
					lst[key] = int32(v * 1000)
				}
			}
			cnt++
		}
		return true
	})
	return
}
